package com.example.jetpacktest.coroutine

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import org.junit.Test

class Test004 {

    @Test
    fun test001(){
        runBlocking {
            val ch = Channel<Int>()
            launch {
                (1..5).forEach {
                    ch.send(it)
                }
                ch.close() //关闭通道
            }
            for(x in ch){
                println(x)
            }
            println("Done")
        }
    }

    @Test
    fun test002(){
        runBlocking {
            val ch = Channel<Int>()
            produce{
                for (x in 1..5) send(x * x)
            }.consumeEach {
                println(it)
            }

        }
    }


    @ExperimentalCoroutinesApi
    fun CoroutineScope.produceNumbers(): ReceiveChannel<Int> = produce<Int> {
        var x = 1
        while(true){
            send(x)
            x++
        }
    }

    @ExperimentalCoroutinesApi
    fun CoroutineScope.square(ch: ReceiveChannel<Int>) = produce<Int> {
        for(x in ch){
            send(x * x)
        }
    }


    @Test
    fun test003(){
        runBlocking {
            val ch1 = produceNumbers()
            val ch2 = square(ch1)
            repeat(5){
                val x = ch2.receive()
                println(x)
            }
            println("Done")
            coroutineContext.cancelChildren() //取消子协程
        }
    }


    @ExperimentalCoroutinesApi
    fun CoroutineScope.numbersFrom(start: Int): ReceiveChannel<Int> = produce<Int> {
        var x = start
        while(true){
            send(x)
            x++
        }
    }

    @ExperimentalCoroutinesApi
    fun CoroutineScope.filter(ch: ReceiveChannel<Int>, pri: Int) = produce<Int> {
        for(x in ch){
            if(x % pri != 0){
                send(x)
            }
        }
    }

    @Test
    fun test004(){
        runBlocking {
            var ch1 = numbersFrom(2)
            repeat(10){
                val x = ch1.receive()
                println(x)
                ch1 = filter(ch1, x)
            }
            coroutineContext.cancelChildren()
        }
    }


    @ExperimentalCoroutinesApi
    fun CoroutineScope.fp005(): ReceiveChannel<Int> = produce<Int> {
        var x = 1
        while(true){
            send(x++)
            delay(100)
        }
    }


    fun CoroutineScope.fr005(id: Int, ch: ReceiveChannel<Int>) = launch (Dispatchers.Default){
        /*for(x in ch){
            println("#$id#....$x")
        }*/
        while(true){
            val x = ch.receive()
            println("#$id#....$x")
        }

    }

    @Test
    fun test005(){
        runBlocking {
            val ch = fp005()
            repeat(5){
                fr005(it, ch)
            }
            delay(5000)
            ch.cancel()
        }
    }


    suspend fun f006(ch: SendChannel<String>, data: String, time: Long){
        while(true){
            delay(time)
            ch.send(data)
        }
    }


    @Test
    fun test006(){
        runBlocking {
            val ch: Channel<String> = Channel<String>()
            launch {
                f006(ch, "haha", 500)
            }
            launch {
                f006(ch, "MMM", 1000)
            }
            repeat(10){
                val s = ch.receive()
                println(s)
            }
            coroutineContext.cancelChildren()
        }
    }


    @Test
    fun test007(){
        runBlocking {
            val ch = Channel<Int>(6)
            val job = launch {
                repeat(20){
                    println("send...$it")
                    ch.send(it)
                }
            }
            delay(5000)
            job.cancel()
        }
    }


    data class Ball(var hitCount: Int){}

    suspend fun play(name: String, table: Channel<Ball>){
        for(ball in table){
            ball.hitCount++
            println("$name....$ball")
            delay(300)
            table.send(ball) //再塞回channel中
        }
    }


    @Test
    fun test008(){
        runBlocking {
            val table = Channel<Ball>()
            launch {
                play("ping", table)
            }
            launch {
                play("pong", table)
            }
            table.send(Ball(0))
            delay(10000)
            coroutineContext.cancelChildren()
        }
    }


    @Test
    fun test009(){
        runBlocking {
            withTimeoutOrNull(3000){
                delay(9000)
                println("work1 Done")
            }
            println("work1 timeout")
            withTimeoutOrNull(2000){
                delay(9000)
                println("work2 Done")
            }
            println("work2 timeout")
            println("all down, finish")
        }
    }


    @Test
    fun test010(){
        runBlocking {
            val ch: ReceiveChannel<Unit> = ticker(100, 0)
            var ele = withTimeoutOrNull(1){
                ch.receive()
            }
            println(ele)

            ele = withTimeoutOrNull(70){
                ch.receive()
            }
            println(ele)

            ele = withTimeoutOrNull(30){
                ch.receive()
            }
            println(ele)

            ele = withTimeoutOrNull(50){
                ch.receive()
            }
            println(ele)

            ele = withTimeoutOrNull(50){
                ch.receive()
            }
            println(ele)
        }
    }

}